using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
using System.Reflection;
using System.IO;
using System.Xml.Serialization;
using Xfinity.Console;
using System.Diagnostics;
using System.Xml.Schema;
using Xfinity.Engine;
using System.Runtime.InteropServices;
using System.ComponentModel;

namespace Xfinity.Materials
{
    public enum MaterialType
    {
        None,
        Wood,
        Metal,
        Brick,
        Sand,
        Grass,
        Water,

    }
    public struct Map
    {
        #region Name
        private string texture;

        /// <summary>
        /// Texture.
        /// </summary>
        public string Name
        {
            [DebuggerStepThroughAttribute]
            get { return texture; }
            [DebuggerStepThroughAttribute]
            set { texture = value; }
        }
        #endregion
        #region Transform
        private TextureTransform transform;

        /// <summary>
        /// Transform.
        /// </summary>
        public TextureTransform Transform
        {
            [DebuggerStepThroughAttribute]
            get { return transform; }
            [DebuggerStepThroughAttribute]
            set { transform = value; }
        }
        #endregion
    }
    public struct TextureTransform //: System.Xml.Serialization.IXmlSerializable
    {
        #region Center
        private Vector2 center;

        /// <summary>
        /// Center.
        /// </summary>
        [DefaultValue(typeof(Vector2), "0, 0")]
        public Vector2 Center
        {
            [DebuggerStepThroughAttribute]
            get { return center; }
            [DebuggerStepThroughAttribute]
            set { center = value; }
        }
        #endregion
        #region Scale
        private Vector2 scale;

        /// <summary>
        /// Scale.
        /// </summary>
        [DefaultValue(typeof(Vector2), "1, 1")]
        public Vector2 Scale
        {
            [DebuggerStepThroughAttribute]
            get { return scale; }
            [DebuggerStepThroughAttribute]
            set { scale = value; }
        }
        #endregion
        #region Rotation
        private float rotation;

        /// <summary>
        /// Rotation.
        /// </summary>
        [DefaultValue(0)]
        public float Rotation
        {
            [DebuggerStepThroughAttribute]
            get { return rotation; }
            [DebuggerStepThroughAttribute]
            set { rotation = value; }
        }
        #endregion
        #region Translate
        private Vector2 translate;

        /// <summary>
        /// Translate.
        /// </summary>
        [DefaultValue(typeof(Vector2), "0, 0")]
        public Vector2 Translate
        {
            [DebuggerStepThroughAttribute]
            get { return translate; }
            [DebuggerStepThroughAttribute]
            set { translate = value; }
        }
        #endregion

        #region IXmlSerializable Members
        /*
        public XmlSchema GetSchema()
        {
            return null;
        }

        public void ReadXml(System.Xml.XmlReader reader)
        {
            reader.ReadStartElement("Transform");
            center = Read("Center", reader);
            scale = Read("Scale", reader);
            if (scale == Vector2.Zero)
            {
                scale = Vector2.One;
            }
            reader.ReadStartElement("Rotation");
            rotation = Convert.ToSingle(reader.ReadElementString());
            reader.ReadEndElement();
            translate = Read("Translate", reader);
            reader.ReadEndElement();
        }

        private Vector2 Read(string name, System.Xml.XmlReader reader)
        {
            Vector2 value;

            reader.ReadStartElement(name);
            reader.ReadStartElement("X");
            value.X = Convert.ToSingle(reader.ReadElementString());
            reader.ReadEndElement();
            reader.ReadStartElement("Y");
            value.Y = Convert.ToSingle(reader.ReadElementString());
            reader.ReadEndElement();
            reader.ReadEndElement();
            return value;
        }

        public void WriteXml(System.Xml.XmlWriter writer)
        {
            Write("Center", center, writer);
            Write("Scale", scale, writer);
            writer.WriteStartElement("Rotation");
            writer.WriteValue(rotation);
            writer.WriteEndElement();
            Write("Translate", translate, writer);
        }

        private void Write(string element, Vector2 v, System.Xml.XmlWriter writer)
        {
            writer.WriteStartElement(element);
            writer.WriteStartElement("X");
            writer.WriteValue(v.X);
            writer.WriteEndElement();
            writer.WriteStartElement("Y");
            writer.WriteValue(v.Y);
            writer.WriteEndElement();
            writer.WriteEndElement();
        }*/
        #endregion
    }
    public class Material
    {
        private class MaterialSettings
        {
            public bool lol;
        }
        internal Material(MaterialManager manager)
        {
            this.manager = manager;
        }
        #region Manager
        private MaterialManager manager;

        /// <summary>
        /// Manager.
        /// </summary>
        public MaterialManager Manager
        {
            [System.Diagnostics.DebuggerStepThroughAttribute]
            get { return manager; }
        }
        #endregion

        #region DiffuseMap
        private Map diffuseMap;

        /// <summary>
        /// DiffuseTexture.
        /// </summary>
        public Map DiffuseMap
        {
            [System.Diagnostics.DebuggerStepThroughAttribute]
            get { return diffuseMap; }
            [System.Diagnostics.DebuggerStepThroughAttribute]
            set { diffuseMap = value; }
        }
        #endregion
        #region NormalMap
        private Map normalMap;

        /// <summary>
        /// NormalMap.
        /// </summary>
        public Map NormalMap
        {
            [DebuggerStepThroughAttribute]
            get { return normalMap; }
            [DebuggerStepThroughAttribute]
            set { normalMap = value; }
        }
        #endregion
        #region HeightMap
        private Map heightMap;

        /// <summary>
        /// HeightMap.
        /// </summary>
        public Map HeightMap
        {
            [DebuggerStepThroughAttribute]
            get { return heightMap; }
            [DebuggerStepThroughAttribute]
            set { heightMap = value; }
        }
        #endregion
        #region Colour
        private Vector3 colour;

        /// <summary>
        /// Colour.
        /// </summary>
        public Vector3 Colour
        {
            [DebuggerStepThroughAttribute]
            get { return colour; }
            [DebuggerStepThroughAttribute]
            set { colour = value; }
        }
        #endregion
        #region DetailMap
        private Map detailMap;

        /// <summary>
        /// DetailMap.
        /// </summary>
        public Map DetailMap
        {
            [DebuggerStepThroughAttribute]
            get { return detailMap; }
            [DebuggerStepThroughAttribute]
            set { detailMap = value; }
        }
        #endregion
        #region Contrast
        private float contrast;

        /// <summary>
        /// Contrast.
        /// </summary>
        public float Contrast
        {
            [DebuggerStepThroughAttribute]
            get { return contrast; }
            [DebuggerStepThroughAttribute]
            set { contrast = value; }
        }
        #endregion
        #region EmissiveColour
        private Vector3 emissiveColour;

        /// <summary>
        /// EmissiveColour.
        /// </summary>
        public Vector3 EmissiveColour
        {
            [DebuggerStepThroughAttribute]
            get { return emissiveColour; }
            [DebuggerStepThroughAttribute]
            set { emissiveColour = value; }
        }
        #endregion
        #region MaterialType
        private MaterialType type;

        /// <summary>
        /// Type.
        /// </summary>
        public MaterialType MaterialType
        {
            [DebuggerStepThroughAttribute]
            get { return type; }
            [DebuggerStepThroughAttribute]
            set { type = value; }
        }
        #endregion

        private static Dictionary<MaterialSettings, Effect> compiledEffects = new Dictionary<MaterialSettings, Effect>();
        private static EffectPool pool = new EffectPool();
        private static GraphicsDevice device;
        public Effect RequestEffect()
        {
            Effect e = null;// = new Effect(device, Effect.CompileEffectFromSource(
            return e;
        }


        #region CreateNewMaterial
        /// <summary>
        /// CreateNewMaterial.
        /// </summary>
        [BindCommand("CreateNewMaterial")]
        public static void CreateNewMaterial(string[] commands)
        {
            Random random = new Random();
            IMaterialDescription d = CreateEmptyMaterialDescription();
            PropertyInfo[] infoArray = MaterialManager.MaterialDescriptionType.GetProperties();
            foreach (PropertyInfo info in infoArray)
            {
                if (info.PropertyType == typeof(Vector3))
                {
                    info.SetValue(d, new Vector3(random.Next(255), random.Next(255), random.Next(255)), null);
                    continue;
                }
                else if (info.PropertyType == typeof(string))
                {
                    info.SetValue(d, info.Name + " " + info.PropertyType.FullName, null);
                }
                else if (info.PropertyType == typeof(float))
                {
                    info.SetValue(d, (float)random.NextDouble(), null);
                }
                else
                {
                    if (info.PropertyType.IsValueType)
                    {
                        if (info.PropertyType.IsPrimitive)
                        {
                            if (info.PropertyType.IsAssignableFrom(typeof(int)))
                            {
                                info.SetValue(d, random.Next(), null);
                            }
                            else if (info.PropertyType.IsAssignableFrom(typeof(double)))
                            {
                                info.SetValue(d, 0.0, null);
                            }
                            else
                            {
                                Debug.Fail("Cannot assign to type!");
                            }
                        }
                        else
                        {
                            ConstructorInfo c = info.PropertyType.GetConstructor(Type.EmptyTypes);
                            if (c == null)
                            {
                                continue;
                            }
                            info.SetValue(d, c.Invoke(null), null);
                        }
                    }
                    else
                    {
                        ConstructorInfo constructor = info.PropertyType.GetConstructor(Type.EmptyTypes);
                        if (constructor != null)
                        {
                            info.SetValue(d, constructor.Invoke(null), null);
                        }
                        else
                        {
                            Debug.Fail("Type has no default constructor");
                        }
                    }
                }
            }
            try
            {
                XmlSerializer s = new XmlSerializer(MaterialManager.MaterialDescriptionType);
                using (FileStream stream = File.Open(".\\Material.material", FileMode.Create, FileAccess.Write))
                {
                    s.Serialize(stream, d);
                }
                using (FileStream stream = File.OpenRead(".\\Material.material"))
                {
                    object o = s.Deserialize(stream);
                }
            }
            catch (Exception e)
            {
                Debugger.Break();
            }


        }
        #endregion
        private static IMaterialDescription CreateEmptyMaterialDescription()
        {
            ConstructorInfo i = MaterialManager.MaterialDescriptionType.GetConstructor(Type.EmptyTypes);

            return (IMaterialDescription)MaterialManager.MaterialDescriptionType.GetConstructor(Type.EmptyTypes).Invoke(null);
        }
        public void Save(Stream stream)
        {
            object description = CreateEmptyMaterialDescription();

            foreach (PropertyInfo descriptionField in MaterialManager.MaterialDescriptionType.GetProperties())
            {
                PropertyInfo materialField = typeof(Material).GetProperty(descriptionField.Name, BindingFlags.Instance | BindingFlags.GetProperty);
                descriptionField.SetValue(description, manager.GetNameFromTexture((Texture2D)materialField.GetValue(this, null)), null);
            }
            XmlSerializer serializer = new XmlSerializer(MaterialManager.MaterialDescriptionType);
            serializer.Serialize(stream, description);
        }
        public static Material LoadMaterial(Stream stream, Game game)
        {
            XmlSerializer serializer = new XmlSerializer(MaterialManager.MaterialDescriptionType);
            IMaterialDescription description = (IMaterialDescription)serializer.Deserialize(stream);
            MaterialManager manager = (MaterialManager)game.Services.GetService(typeof(MaterialManager));
            return manager.CreateMaterial(description);
        }
    }
}
